/*
* H264FramedSource.cpp
*
*  Created on: 2015年11月27日
*      Author: terry
*/

#include "H264FramedSource.h"
#include "H264NaluParser.h"
#include "TFileUtil.h"
#include "GroupsockHelper.hh"
#include "CLog.h"
#include "StopWatch.h"

static const size_t MAX_QUEUE_SIZE = 30;

namespace av
{

	H264FramedSource::H264FramedSource(UsageEnvironment& env, MediaSourcePtr& mediaSource, MediaFormat& fmt, int clockRate) :
		FramedSource(env),
		m_mediaSource(mediaSource),
		m_fmt(fmt),
		m_clockRate(clockRate),
		m_taskID(),
		m_streamEvent(),
		m_pktOffset(),
		m_lastPts()
	{
		m_linkSink.reset(new LinkMediaSink(this));
		m_mediaSource->addSink(m_linkSink);

		m_needIFrame = false;

	}

	H264FramedSource::~H264FramedSource()
	{
		if (m_mediaSource)
		{
			m_mediaSource->removeSink(m_linkSink);
		}

		m_linkSink->resetSink(NULL);

		m_pktQueue.clear();

		m_pkt.reset();
	}

	void H264FramedSource::doGetNextFrame()
	{
		if (m_streamEvent == av::STREAM_EVENT_END)
		{
			handleClosure();
			return;
		}

		if (!m_taskID)
		{
			m_taskID = envir().taskScheduler().createEventTrigger(taskFunc);
		}

		if (m_pktQueue.size() > 0)
		{
			bool working = onTask();
		}
		else
		{

		}

	}

	void H264FramedSource::doStopGettingFrames()
	{
		if (m_taskID)
		{
			envir().taskScheduler().deleteEventTrigger(m_taskID);
			m_taskID = EventTriggerId();
		}

		m_pktQueue.clear();
	}

	void H264FramedSource::onMediaFormat(const MediaFormat& fmt)
	{

	}

	void H264FramedSource::onMediaPacket(MediaPacketPtr& pkt)
	{
		if (!pkt || !pkt->isVideo())
		{
			return;
		}

		//comn::FileUtil::write(pkt->data, pkt->size, "out.hevc", true);

		if (m_needIFrame)
		{
			if (pkt->isKey())
			{
				m_needIFrame = false;
			}
		}

		if (m_needIFrame)
		{
			return;
		}

		size_t queSize = m_pktQueue.push(pkt);

		if (queSize > MAX_QUEUE_SIZE)
		{
			CLog::info("#video too many pkt. queue:%d\n", queSize);
			m_pktQueue.clear();
			envir().taskScheduler().triggerEvent(m_taskID, this);
		}
		else if (queSize > MAX_QUEUE_SIZE / 3)
		{
			envir().taskScheduler().triggerEvent(m_taskID, this);
		}
		else if (queSize == 1)
		{
			envir().taskScheduler().triggerEvent(m_taskID, this);
		}

	}

	void H264FramedSource::onMediaEvent(int event)
	{
		m_streamEvent = event;

	}

	void H264FramedSource::taskFunc(void* clientData)
	{
		H264FramedSource* pSource = (H264FramedSource*)clientData;
		if (pSource)
		{
			pSource->doGetNextFrame();
		}
	}

	bool H264FramedSource::onTask()
	{
		if (!isCurrentlyAwaitingData())
		{
			return false;
		}

		if (isPacketEmpty())
		{
			m_pktOffset = 0;
			if (!m_pktQueue.pop(m_pkt))
			{
				return false;
			}
		}

		if (m_pkt->size <= 0)
		{
			return false;
		}

		av::MediaPacket& pkt = *m_pkt.get();

		if (m_pktOffset >= pkt.size)
		{
			m_pktOffset = 0;
			m_pkt.reset();
			return false;
		}

		uint8_t* data = pkt.data + m_pktOffset;
		int	size = pkt.size - m_pktOffset;

		int prefix = 0;
		NaluPacket nalu;
		H264NaluParser::parseNalu(data, size, nalu);

		data += nalu.prefix;
		size -= nalu.prefix;

		size_t idxEnd = H264NaluParser::findH264StartCode(data, size, 0);
		int len = size;
		if (idxEnd != -1)
		{
			len = idxEnd;
		}

		m_pktOffset = (data + len) - pkt.data;

		memcpy(fTo, data, len);
		fFrameSize = len;

		int64_t pts = pkt.pts * 1000000 / m_clockRate;

		if (m_lastPts == 0)
		{
			m_lastPts = pts;
		}

		int64_t duration = pts - m_lastPts;

		if ((fPresentationTime.tv_sec == 0) && (fPresentationTime.tv_usec == 0))
		{
			gettimeofday(&fPresentationTime, NULL);
			fDurationInMicroseconds = 0;
			m_lastPts = pts;
		}
		else
		{
			fDurationInMicroseconds = 0;

			long usec = (long)(duration + fPresentationTime.tv_usec);
			fPresentationTime.tv_sec += usec / 1000000;
			fPresentationTime.tv_usec = usec % 1000000;
		}

		m_lastPts = pts;

		size_t queSize = m_pktQueue.size();
		if (queSize > 5) {
			CLog::info("video pts:%lld. fpts:%ld.%ld, queue:%d\n",
				pts, fPresentationTime.tv_sec, fPresentationTime.tv_usec / 1000,
				queSize);
		}

		afterGetting(this);

		return true;
	}

	MediaSourcePtr H264FramedSource::getMediaSource()
	{
		return m_mediaSource;
	}

	bool H264FramedSource::isPacketEmpty()
	{
		if (!m_pkt || m_pkt->size <= 0)
		{
			return true;
		}
		return (m_pktOffset >= m_pkt->size);
	}



} /* namespace av */
